package cn.edu.zisu.common.util;

import com.google.common.collect.Lists;
import cn.edu.zisu.common.model.entity.BaseEntity;
import cn.edu.zisu.common.model.enumeration.SortOrderEnum;
import cn.edu.zisu.common.model.exception.QueryException;
import cn.edu.zisu.common.model.info.ResultInfo;
import cn.edu.zisu.common.model.request.ListPagingPagerParam;
import cn.edu.zisu.common.model.request.ListPagingParam;
import cn.edu.zisu.common.model.request.PagingParam;
import cn.edu.zisu.common.model.response.ListPagingPagerData;
import cn.edu.zisu.common.util.model.PageRunner;
import org.apache.commons.collections4.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * @author : Sonya
 * @date : 2020/3/23 19:40
 */
public class DomainPagingUtils {
    /**
     * 统计
     *
     * @param pagingPagerParam
     * @param tenantId
     * @param <T>
     * @param <R>
     * @param <E>
     * @param <P>
     * @return
     * @throws QueryException
     */
    public static <T, R extends BaseEntity, E, P extends ListPagingParam> Integer pagingCount(ListPagingPagerParam<T, R, E, P> pagingPagerParam, String tenantId) throws QueryException {
        Integer count = 0;
        try {
            // 获取反射方法
            Method countMethod = pagingPagerParam.getMapper().getClass().getMethod(pagingPagerParam.getCountMethodName(),
                    pagingPagerParam.getQueryParam().getClass(), String.class);
            count = (Integer) countMethod.invoke(pagingPagerParam.getMapper(), pagingPagerParam.getQueryParam(), tenantId);
            return count;
        } catch (ClassCastException | NoSuchMethodException | IllegalAccessException e) {
            throw new QueryException("方法参数异常:" + e.getMessage());
        } catch (InvocationTargetException e) {
            throw new QueryException("方法调用异常:" + e.getTargetException().getMessage());
        }
    }

    /**
     * 根据id查
     * 不再推荐此分页查询方法
     *
     * @param pagingPagerParam
     * @param tenantId
     * @param <T>
     * @param <R>
     * @param <E>
     * @param <P>
     * @return
     * @throws QueryException
     */
    @Deprecated
    public static <T, R extends BaseEntity, E, P extends ListPagingParam> ListPagingPagerData<R> listPagingPager(ListPagingPagerParam<T, R, E, P> pagingPagerParam, String tenantId) throws QueryException {
        ResultInfo<String> resultInfo = pagingPagerParam.checkParams();
        if (!resultInfo.isRight()) {
            throw new QueryException(resultInfo.getMessage());
        }
        Integer count = DomainPagingUtils.pagingCount(pagingPagerParam, tenantId);
        // 无分页参数
        if (pagingPagerParam.getQueryParam().getPagingParam() == null) {
            pagingPagerParam.getQueryParam().setPagingParam(PagingParam.builder().start(0).length(count).build());
        }
        if (pagingPagerParam.getQueryParam().getPagingParam().getStart() >= count) {
            return ListPagingPagerData.EMPTY;
        }
        PagingParam pagingParam = pagingPagerParam.getQueryParam().getPagingParam();
        List<R> resultList = Lists.newArrayListWithCapacity(pagingParam.getLength());
        try {
            PageRunner runner = PageRunner.parseRunner(pagingParam);
            if (pagingPagerParam.getQueryParam().getPagingParam().getStart() > 0) {
                // 直接拿到start的上一个id
                // select min(id) from table where id < #lastId and xxx order by id desc limit #start;
                Method lastValMethod = pagingPagerParam.getMapper().getClass().getMethod(pagingPagerParam.getLastValMethodName(),
                        pagingPagerParam.getQueryParam().getClass(), Long.class, Integer.class,
                        pagingPagerParam.getFieldEnum().getClass(), SortOrderEnum.class, String.class);
                Long lastId = (Long) lastValMethod.invoke(
                        pagingPagerParam.getMapper(), pagingPagerParam.getQueryParam(), runner.getLastId(),
                        pagingPagerParam.getQueryParam().getPagingParam().getStart(),
                        pagingPagerParam.getFieldEnum(), pagingPagerParam.getOrderByDirection(),
                        tenantId
                );
                runner.setLastId(lastId);
            }
            // 前n页查, PageRunner.PAGE_SIZE
            // select xxx from table where id < #lastId and xxx order by id desc limit #querySize;
            // 分页逻辑处理
            Method listMethod = pagingPagerParam.getMapper().getClass().getMethod(pagingPagerParam.getListMethodName(),
                    pagingPagerParam.getQueryParam().getClass(), Long.class, Integer.class, Integer.class,
                    pagingPagerParam.getFieldEnum().getClass(), SortOrderEnum.class, String.class);
            for (int i = 0; i < runner.getTimes(); i++) {
                List<R> objList = (List<R>) listMethod.invoke(
                        pagingPagerParam.getMapper(), pagingPagerParam.getQueryParam(), runner.getLastId(),
                        pagingPagerParam.getPageSize(), runner.getOffset(),
                        pagingPagerParam.getFieldEnum(), pagingPagerParam.getOrderByDirection(),
                        tenantId
                );
                if (CollectionUtils.isNotEmpty(objList)) {
                    R r = objList.get(objList.size() - 1);
                    runner.setLastId(r.getId());
                    resultList.addAll(objList);
                }
            }
            // 最后一页 runner.getFinalSize()
            if (runner.getFinalSize() > 0) {
                List<R> objList = (List<R>) listMethod.invoke(
                        pagingPagerParam.getMapper(), pagingPagerParam.getQueryParam(), runner.getLastId(),
                        runner.getFinalSize(), runner.getOffset(),
                        pagingPagerParam.getFieldEnum(), pagingPagerParam.getOrderByDirection(),
                        tenantId
                );
                resultList.addAll(objList);
            }
        } catch (ClassCastException | NoSuchMethodException | IllegalAccessException e) {
            throw new QueryException("方法参数异常:" + e.getMessage());
        } catch (InvocationTargetException e) {
            throw new QueryException("方法调用异常:" + e.getTargetException().getMessage());
        }
        return new ListPagingPagerData<>(count, resultList);
    }


    /**
     * @param pagingPagerParam
     * @param tenantId
     * @return com.xinheyun.model.response.ListPagingPagerData<R>
     * @Desc: 优化分页查询，去除按主键id段查询的逻辑，避免大表多次遍历，可能需要给部分查询条件创建索引
     * @author cuijh
     * @date 2020/8/12 16:43
     */
    public static <T, R extends BaseEntity, E, P extends ListPagingParam> ListPagingPagerData<R> listPagingPagerV2(ListPagingPagerParam<T, R, E, P> pagingPagerParam, String tenantId) throws QueryException {
        ResultInfo<String> resultInfo = pagingPagerParam.checkParams();
        if (!resultInfo.isRight()) {
            throw new QueryException(resultInfo.getMessage());
        }
        // 记录总数
        Integer count = DomainPagingUtils.pagingCount(pagingPagerParam, tenantId);
        // 无分页参数
        if (pagingPagerParam.getQueryParam().getPagingParam() == null) {
            pagingPagerParam.getQueryParam().setPagingParam(PagingParam.builder().start(0).length(count).build());
        }
        if (pagingPagerParam.getQueryParam().getPagingParam().getStart() >= count) {
            return ListPagingPagerData.EMPTY;
        }
        List<R> resultList = null;
        try {
            // 分页逻辑处理
            Method pageMethod = pagingPagerParam.getMapper().getClass().getMethod(pagingPagerParam.getPageByParam(),
                    pagingPagerParam.getQueryParam().getClass(), pagingPagerParam.getFieldEnum().getClass(), SortOrderEnum.class, String.class);
            resultList = (List<R>) pageMethod.invoke(
                    pagingPagerParam.getMapper(), pagingPagerParam.getQueryParam(), pagingPagerParam.getFieldEnum(), pagingPagerParam.getOrderByDirection(),
                    tenantId
            );
        } catch (ClassCastException | NoSuchMethodException | IllegalAccessException e) {
            throw new QueryException("方法参数异常:" + e.getMessage());
        } catch (InvocationTargetException e) {
            throw new QueryException("方法调用异常:" + e.getTargetException().getMessage());
        }
        return new ListPagingPagerData<>(count, resultList == null ? Lists.newArrayList() : resultList);
    }
}
